package com.rm.zeyou.pay.alipay.service.impl;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.kernel.util.ResponseChecker;
import com.alipay.easysdk.payment.app.models.AlipayTradeAppPayResponse;
import com.alipay.easysdk.payment.common.models.AlipayDataDataserviceBillDownloadurlQueryResponse;
import com.alipay.easysdk.payment.common.models.AlipayTradeFastpayRefundQueryResponse;
import com.alipay.easysdk.payment.common.models.AlipayTradeQueryResponse;
import com.alipay.easysdk.payment.common.models.AlipayTradeRefundResponse;
import com.alipay.easysdk.payment.facetoface.models.AlipayTradePrecreateResponse;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import com.alipay.easysdk.payment.wap.models.AlipayTradeWapPayResponse;
import com.rm.zeyou.exception.ErrorCodeException;
import com.rm.zeyou.exception.ErrorCodeExceptionEnum;
import com.rm.zeyou.mapper.ZfbpayConfigMapper;
import com.rm.zeyou.pay.alipay.entity.AlipayEntity;
import com.rm.zeyou.pay.alipay.service.AlipayService;
import com.rm.zeyou.util.check.CheckEmptyUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;


/**
 * 支付宝支付服务实现类
 *
 * @author zhangkechen
 * @since 2022/10/6 20:07
 * <p>
 * 支付类
 */

@Service
@Slf4j
@Transactional(rollbackFor = RuntimeException.class)
public class AlipayServiceImpl implements AlipayService {
    @Resource
    private AlipayEntity alipayEntity;
    @Resource
    private ZfbpayConfigMapper zfbpayConfigMapper;

    /**
     * 设置支付宝配置信息
     *
     * @return 配置信息
     */
    @Override
    public Config getOptions(String notifyUrl) {
        Config config = new Config();
        String appId = zfbpayConfigMapper.selectOneData("AppId");
        String appPrivateKey = zfbpayConfigMapper.selectOneData("AppPrivateKey");
        String publicKey = zfbpayConfigMapper.selectOneData("PublicKey");
        String merchantCertPath = zfbpayConfigMapper.selectOneData("merchantCertPath");
        String alipayCertPath = zfbpayConfigMapper.selectOneData("alipayCertPath");
        String alipayRootCertPath = zfbpayConfigMapper.selectOneData("alipayRootCertPath");
        String zfbModel = zfbpayConfigMapper.selectOneData("zfb_model");
        config.protocol = alipayEntity.getProtocol();
        config.gatewayHost = alipayEntity.getGatewayHost();
        config.signType = alipayEntity.getSignType();
        config.appId = appId;
        config.notifyUrl = notifyUrl;
        config.merchantPrivateKey = appPrivateKey;
        //证书非必填
        if (Integer.parseInt(zfbModel) == 2){
            config.merchantCertPath = merchantCertPath;
            config.alipayCertPath = alipayCertPath;
            config.alipayRootCertPath = alipayRootCertPath;
        }else if (Integer.parseInt(zfbModel) == 1){
            //注：如果采用非证书模式，则无需赋值上面的三个证书路径，改为赋值如下的支付宝公钥字符串即可
            config.alipayPublicKey = publicKey;
        } else {
            throw new RuntimeException("配置信息有误：证书模式配置不完整或未配置公钥模式，请检查！");
        }
        //可设置AES密钥，调用AES加解密相关接口时需要（可选）
        config.encryptKey = alipayEntity.getEncryptKey();
        return config;
    }

    /**
     * 统一支付服务接口（当面付、电脑网站支付、手机网站支付、APP支付）
     *
     * @param payType     支付类型：当面付（faceToFace），电脑网站支付（page），手机网站支付（wap），APP支付（app）
     * @param subject     商品名称
     * @param outTradeNo  商户订单号：商户内唯一
     * @param totalAmount 总金额（单位：元），实例：12.34
     * @param returnUrl   支付成功后跳转页面（只针对网站支付有效）
     * @param quitUrl     支付取消跳转页面（只针对手机网站支付有效）
     * @param notifyUrl   支付成功后回掉接口
     * @return 返回结果：当面付为二维码链接，其他均为网站Body代码
     */
    @Override
    public String pay(String payType, String subject, String outTradeNo, String totalAmount, String returnUrl, String quitUrl, String notifyUrl) throws Exception {

        // 必填信息不能为空
        if (CheckEmptyUtil.isEmpty(subject) || CheckEmptyUtil.isEmpty(outTradeNo) || CheckEmptyUtil.isEmpty(totalAmount)) {
            throw new ErrorCodeException(ErrorCodeExceptionEnum.ALIPAY_PRODUCT_ERROR);
        }
        /**
         * 设置参数
         */
        Factory.setOptions(this.getOptions(notifyUrl));
        /**
         * 根据类型创建支付
         */
        switch (payType) {
            /**
             * 当面付
             */
            case "FACE":
                /**
                 * 创建订单
                 */
                AlipayTradePrecreateResponse faceToFaceResponse = Factory.Payment.FaceToFace().preCreate(subject, outTradeNo, totalAmount);
                /**
                 * 返回支付信息
                 */
                if (ResponseChecker.success(faceToFaceResponse)) {
                    return faceToFaceResponse.getQrCode();
                }
                break;
            /**
             * 电脑网站支付
             */
            case "H5":
                if (CheckEmptyUtil.isEmpty(returnUrl)) {
                    throw new ErrorCodeException(ErrorCodeExceptionEnum.ALIPAY_RETURN_URL_NULL);
                }
                /**
                 * 创建订单
                 */
                AlipayTradePagePayResponse pageResponse = Factory.Payment.Page().pay(subject, outTradeNo, totalAmount, returnUrl);
                /**
                 * 返回支付信息
                 */
                if (ResponseChecker.success(pageResponse)) {
                    return pageResponse.getBody();
                }
                break;
            /**
             * 手机网站支付
             */
            case "WAP":
                if (CheckEmptyUtil.isEmpty(returnUrl) || CheckEmptyUtil.isEmpty(quitUrl)) {
                    throw new ErrorCodeException(ErrorCodeExceptionEnum.ALIPAY_RETURN_AND_QUIT_URL_NULL);
                }
                /**
                 * 创建订单
                 */
                AlipayTradeWapPayResponse wapResponse = Factory.Payment.Wap().pay(subject, outTradeNo, totalAmount, quitUrl, returnUrl);
                /**
                 * 返回支付信息
                 */
                if (ResponseChecker.success(wapResponse)) {
                    return wapResponse.getBody();
                }
                break;
            /**
             * APP支付
             */
            case "APP":
                /**
                 * 创建订单
                 */
                AlipayTradeAppPayResponse appResponse = Factory.Payment.App().pay(subject, outTradeNo, totalAmount);
                /**
                 * 返回支付信息
                 */
                if (ResponseChecker.success(appResponse)) {
                    return appResponse.getBody();
                }
                break;
            /**
             * 其他类型：错误
             */
            default:
                throw new ErrorCodeException(ErrorCodeExceptionEnum.ALIPAY_TYPE_ERROR);
        }

        throw new ErrorCodeException(ErrorCodeExceptionEnum.ALIPAY_ERROR);
    }

    /**
     * 查询支付订单接口
     *
     * @param tradeNo    特殊可选：支付宝交易号（订单号）
     * @param outTradeNo 特殊可选：商家订单号
     * @apiNote tradeNo 和 outTradeNo 不能同时为空。同时存在优先取 tradeNo。
     */
    @Override
    public AlipayTradeQueryResponse queryPay(String tradeNo, String outTradeNo) throws Exception {
        // 校验参数
        if (CheckEmptyUtil.isEmpty(tradeNo) && CheckEmptyUtil.isEmpty(outTradeNo)) {
            throw new ErrorCodeException(ErrorCodeExceptionEnum.ALIPAY_QUERY_ERROR);
        }
        // 设置参数
        Factory.setOptions(this.getOptions(null));
        // 执行查询
        AlipayTradeQueryResponse response = Factory.Payment.Common().optional("trade_no", tradeNo).query(outTradeNo);
        // 请求成功（即返回信息中没有sub_code）
        if (ResponseChecker.success(response)) {
            return response;
        } else {
            throw new ErrorCodeException(ErrorCodeExceptionEnum.ALIPAY_QUERY_ERROR);
        }

    }

    /**
     * 退款接口（支持部分退款）
     *
     * @param tradeNo      特殊可选：商户订单号
     * @param outTradeNo   特殊可选：商户订单号
     * @param refundAmount 必填：退款金额
     * @param reason       可选：退款原因
     * @apiNote tradeNo 和 outTradeNo 不能同时为空。同时存在优先取 tradeNo。
     */
    @Override
    public AlipayTradeRefundResponse refund(String tradeNo, String outTradeNo, String refundAmount, String reason) throws Exception {
        // 校验参数
        if (CheckEmptyUtil.isEmpty(tradeNo) && CheckEmptyUtil.isEmpty(outTradeNo)) {
            throw new ErrorCodeException(ErrorCodeExceptionEnum.ALIPAY_QUERY_ERROR);
        }
        // 设置参数
        Factory.setOptions(this.getOptions(null));
        // 生成唯一的款请求号
        String outRequestNo = IdUtil.simpleUUID();
        // 发起请求
        AlipayTradeRefundResponse response = Factory.Payment.Common()
                // 支付宝交易号
                .optional("out_trade_no", tradeNo)
                // 退款原因
                .optional("refund_reason", reason)
                // 退款请求号
                .optional("out_request_no", outRequestNo)
                // 执行退款
                .refund(outTradeNo, refundAmount);
        // 如果请求成功（即返回信息中没有sub_code）
        log.info("支付宝退款返回:{}", JSON.toJSONString(response,true));
        if (ResponseChecker.success(response)) {
            return response;
        } else {
            throw new ErrorCodeException(ErrorCodeExceptionEnum.ALIPAY_REFUND_ERROR);
        }

    }

    /**
     * 查询退款
     *
     * @param outTradeNo   必填：商户订单号
     * @param outRequestNo 必填：退款请求号
     */
    @Override
    public AlipayTradeFastpayRefundQueryResponse queryRefund(String outTradeNo, String outRequestNo) throws Exception {
        // 设置参数
        Factory.setOptions(this.getOptions(null));
        // 如果请求号为空，则表示全额退款，设置请求号位商家订单号
        if (CheckEmptyUtil.isEmpty(outRequestNo)) {
            outRequestNo = outTradeNo;
        }
        // 发起请求
        AlipayTradeFastpayRefundQueryResponse response = Factory.Payment.Common().queryRefund(outTradeNo, outRequestNo);
        // 如果请求成功（即返回信息中没有sub_code）
        if (ResponseChecker.success(response)) {
            if (null != response.refundAmount) {
                return response;
            } else {
                throw new ErrorCodeException(ErrorCodeExceptionEnum.ALIPAY_QUERY_REFUND_ERROR);
            }
        } else {
            throw new ErrorCodeException(ErrorCodeExceptionEnum.ALIPAY_QUERY_REFUND_ERROR);
        }

    }

    /**
     * 下载对账单（不能查询当天或当月的)
     *
     * @param date 必填：交易的具体日期（如2022-01-01）或月份（2021-12）
     * @return 获取成功：下载URL  发生错误：错误代码，错误描述
     */
    @Override
    public AlipayDataDataserviceBillDownloadurlQueryResponse downloadBill(String date) throws Exception {
        // 设置参数
        Factory.setOptions(this.getOptions(null));
        // 发送请求
        AlipayDataDataserviceBillDownloadurlQueryResponse response = Factory.Payment.Common().downloadBill("trade", date);

        // 如果请求成功（即返回信息中没有sub_code）
        if (ResponseChecker.success(response)) {
            return response;
        } else {
            throw new ErrorCodeException(ErrorCodeExceptionEnum.ALIPAY_DOWNLOAD_ORDER_ERROR);
        }
    }


}


